/**************************************************************************************
 *  Copyright (c)  2010  Leszek Karcz, Changmin Bae.
 *  Permission is granted to copy, distribute and/or modify this document
 *  under the terms of the GNU Free Documentation License, Version 1.3
 *  or any later version published by the Free Software Foundation;
 *  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
 *  A copy of the license is included in the section entitled "GNU
 *  Free Documentation License". 
 **************************************************************************************/
package data;
import data.tileables.*;
import main.Clock;

/**************************************************************************************
 * Author(s): Leszek Karcz, Changmin Bae
 * This class creates a connection between rest of the data classes and view classes
 **************************************************************************************/
public class GameplayPlayerData
{
	private Map map;
	private VisibilityMap visibilityMap;
	private Clock clock;
	private Player player;
	private int height;
	private int width;
	private int cursorX;
	private int cursorY;
	private int displayResourceMode=1;
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz, Changmin Bae
	 * GameplayViewData constructor
	 **************************************************************************************/
	public GameplayPlayerData(Map map, Clock clock, Player player)
	{
		this.map=map;
		this.clock=clock;
		this.width=map.getWidth();
		this.height=map.getHeight();
		this.visibilityMap=new VisibilityMap(this.width,this.height);
		
		this.player=player;
		this.player.setGameplayData(this);		
		if(verifyCursor(player.getStartPosition()[0],player.getStartPosition()[1]))
		{
			this.cursorX=player.getStartPosition()[0];
			this.cursorY=player.getStartPosition()[1];
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns whether the clock is running
	 **************************************************************************************/
	public boolean isClockRunning()
	{
		return this.clock.isRunning();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns terrain image filename for a tile at [x,y] coordinates
	 **************************************************************************************/
	public String[] getTileTerrainData(int x, int y, boolean relative)
	{
		if(relative)
		{
			return getTileTerrainData(x-4+this.cursorX,y-2+this.cursorY, false);
		}
		else
		{
			if(verifyCursor(x,y))
			{
				String decal=this.map.getDecalImageAt(x,y);
					return new String[]{"tile_"+map.getTerrainAt(x,y),decal};
			}
			else
			{
				return new String[]{"tile_empty"};
			}
		}				
	}
		
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Orders the player to create a rally
	 **************************************************************************************/
	public void createRally()
	{
		this.player.createRally(cursorX, cursorY);
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns resource images for a tile at [x,y] coordinates
	 **************************************************************************************/
	public String[] getTileResourceImages(int x, int y, boolean relative)
	{	
		if(relative)
		{
			return getTileResourceImages(x-4+this.cursorX,y-2+this.cursorY, false);
		}
		else
		{
			if(verifyCursor(x,y))
			{
				if(this.getVisibilityStatus(x, y, false)[1])//resources discovered
				{
					if(this.getVisibilityStatus(x, y, false)[2])//visible
					{
						Resources resources=this.map.getResourcesAt(x, y);
						if(resources!=null)
						{
							return resources.getImageData();
						}
						else
						{
							return new String[]{"","",""};
						}						
					}
					else//not currently visible
					{
						return this.visibilityMap.getResourceImagesAt(x, y);
					}					
				}
				else //resources not discovered
				{
					return new String[]{"","",""};
				}
			}
			else
			{
				return new String[]{"","",""};
			}
		}				
	}
	
	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns resource amounts or worker count for a tile at [x,y] coordinates
	 **************************************************************************************/
	public String[] getTileResourceData(int x, int y, boolean relative)
	{	
		if(relative)
		{
			return getTileResourceData(x-4+this.cursorX,y-2+this.cursorY, false);
		}
		else
		{
			if(verifyCursor(x,y))
			{
				if(this.getVisibilityStatus(x, y, false)[1])//resources discovered
				{
					if(this.getVisibilityStatus(x, y, false)[2])//visible
					{
						if(this.displayResourceMode==0) //resource data hidden
						{
							return new String[]{"","",""};
						}
						else if(this.displayResourceMode==1) //get resource amounts
						{
							Resources resources=this.map.getResourcesAt(x, y);
							if(resources!=null)
							{
								return resources.getResourceData();
							}
							else
							{
								return new String[]{"","",""};
							}
						}
						else if(this.displayResourceMode==2) //get worker count
						{
							Resources resources=this.map.getResourcesAt(x, y);
							if(resources!=null)
							{
								return resources.getWorkersData(this.player.getID());
							}
							else
							{
								return new String[]{"","",""};
							}
						}	
						else
						{
							return new String[]{"","",""};
						}
					}
					else//not currently visible
					{
						if(this.displayResourceMode==1) //resource amounts visible
						{
							return this.visibilityMap.getResourceDataAt(x, y);	
						}
						else
						{
							return new String[]{"","",""};
						}
					}					
				}
				else
				{
					return new String[]{"","",""};
				}
			}
			else
			{
				return new String[]{"","",""};
			}
		}				
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns image of the top item at [x,y] coordinates
	 **************************************************************************************/
	public String getTopItemData(int x, int y, boolean relative)
	{
		if(relative)
		{
			return getTopItemData(x-4+this.cursorX,y-2+this.cursorY, false);
		}
		else
		{
			if(verifyCursor(x,y))
			{
				
					if(this.getVisibilityStatus(x, y, false)[2])//visible
					{
						Item item=this.map.getTopItemAt(x, y);		
						String itemImage="";
						if(item!=null)
						{
							itemImage=item.getImage();
						}
						return itemImage;
					}
					else//not currently visible
					{
						return this.visibilityMap.getTopItemDataAt(x, y);
					}					
			}
			else
			{
				return "";
			}
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns true if the tile at [x,y] coordinates is passable
	 **************************************************************************************/	
	public boolean tileIsPassable(int x, int y)
	{
		return this.map.isPassableAt(x,y,this.player.getID());
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns true if there is a structure at [x,y] coordinates
	 **************************************************************************************/	
	public boolean tileHasStructure(int x, int y)
	{
		return this.map.hasStructureAt(x,y);
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns image filename and highlight filename
	 **************************************************************************************/
	public String[][] getTileImageData(int x, int y, boolean relative)
	{
		if(relative)
		{
			return getTileImageData(x-4+this.cursorX,y-2+this.cursorY, false);
		}
		else
		{
			if(verifyCursor(x,y))
			{
				if(this.getVisibilityStatus(x, y, false)[2])
				{
					String[][] imageData = new String[4][];
					
					Controllable[] controllables = new Controllable[4];
					for(int i=0;i<controllables.length;i++)
					{
						controllables[i]=null;
					}
					
					controllables[0]=this.map.getTopControllableAt(x, y, 0);
					controllables[1]=this.map.getTopControllableAt(x, y, 1);
					controllables[2]=this.map.getTopControllableAt(x, y, 2);
					controllables[3]=this.map.getTopControllableAt(x, y, 3);
					
					for(int i=0;i<controllables.length;i++)
					{
						if(controllables[i]!=null)
						{
							imageData[i]=controllables[i].getImageData(this.player.getID());
						}
					}				
					
					return imageData;					
				}
				else
				{
					return new String[][]{this.visibilityMap.getStructureDataAt(x, y)};	
				}
			}
			else
			{
				return new String[][]{};
			}
		}		
	}
		
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns image and name of currently selected controllable
	 **************************************************************************************/
	public String[] getSelectedTypeData()
	{
		if(this.player.getSelected()!=null)
		{
			return new String[]{this.player.getSelected().getIcon(),this.player.getSelected().getType()};
		}
		else
		{
			return new String[]{"",""};
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * resets the selected controllable comand mode
	 **************************************************************************************/
	public void selectedResetCommandMode()
	{
		if(this.player.getSelected()!=null)
		{
			this.player.getSelected().resetCommandMode();
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns String list of stats of currenlty selected controllable
	 **************************************************************************************/
	public String[] getSelectedStatsData()
	{
		if(this.player.getSelected()!=null)
		{
			return this.player.getSelected().getStatsText();
		}
		else
		{
			return new String[]{""};
		}		
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz, Changmin Bae
	 * returns String list of commands of currenlty selected controllable
	 **************************************************************************************/
	public String[] getSelectedCommands()
	{
		if(this.player.getSelected()!=null)
		{
			return this.player.getSelected().getCommandsText();
		}
		else
		{
			return new String[]{""};
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz, Changmin Bae
	 * returns selected command
	 **************************************************************************************/
	public int getSelectedCommand()
	{
		if(this.player.getSelected()!=null)
		{
			return this.player.getSelected().getSelectedCommand();
		}
		else
		{
			return 0;
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz, Changmin Bae
	 * modifies the current selection
	 **************************************************************************************/
	public void changeSelection(int mode, boolean next)
	{
		if(mode==0) //controllable types
		{
			
		}	
		else if(mode==1) //controllable sub-types
		{
			
		}
		else if(mode==2) //controllables within certain sub-type
		{
			
		}
		else if(mode==3) //selected controllable commands
		{
			if(this.player.getSelected()!=null)
			{
				if(next)
				{
					this.player.getSelected().selectNextCommand();
				}
				else
				{
					this.player.getSelected().selectPreviousCommand();
				}
			}
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns list of all units on tile at [x,y] coordinates
	 **************************************************************************************/	
	public String[][] getTileUnitsData(int x, int y, boolean relative)
	{
		if(relative)
		{
			return getTileUnitsData(x-4+this.cursorX,y-2+this.cursorY, false);
		}
		else
		{
			LinkedList<Unit> units=this.map.getUnitsAt(x, y);
			if(units.getSize()>0)
			{
				String[][] unitsData = new String[units.getSize()][];
				units.setCurrent(0);
				for(int i=0;i<units.getSize();i++)
				{
					unitsData[i]=units.peekCurrent().getIconData(this.player.getID()); 
					units.next();
				}				
				return unitsData;
			}
			else
			{
				return new String[][]{};
			}		
		}
	}	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns:
	 * -whether the tile has been discovered
	 * -whether the resources had been discovered
	 * -whether the tile is currently visible
	 **************************************************************************************/
	public boolean[] getVisibilityStatus(int x, int y, boolean relative)
	{
		if(relative)
		{
			return getVisibilityStatus(x-4+this.cursorX,y-2+this.cursorY, false);
		}
		else
		{
			if(verifyCursor(x,y))
			{
				return this.visibilityMap.getVisibilityStatusAt(x, y, clock.getTick());	
			}
			else
			{
				return new boolean[]{false,false,false};
			}
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Discovers a tile at [x,y] coordinates, and updates it's "last viewed" status and data
	 **************************************************************************************/
	public void refreshTile(int x, int y, boolean prospectMode)
	{
		//get structure data
		Structure structure = map.getStructureAt(x, y);
		String structureImage="";
		int structureOwnerID=0;
		int structureHealthPercentage=0;
		if(structure!=null)
		{
			structureImage=structure.getImage();
			structureOwnerID=structure.getPlayerID();
			structureHealthPercentage=structure.getHealthPercentage();
		}
		
		//get item data
		Item item=this.map.getTopItemAt(x,y);
		String itemImage="";
		if(item!=null)
		{
			itemImage=item.getImage();
		}
		
		//get resource data
		String[] resourceImages=new String[]{"","",""};
		String[] resourceData=new String[]{"","",""};
		Resources resources=this.map.getResourcesAt(x,y);
		if(resources!=null)
		{
			resourceImages=resources.getImageData();
			resourceData=resources.getResourceData();
		}
		this.visibilityMap.refreshTile(x,y,this.clock.getTick(), structureImage, structureOwnerID , structureHealthPercentage, itemImage, resourceImages, resourceData, prospectMode);
	}	
		
	//debug class to manually populate data
	public void refreshTile(int x, int y, String structure, int structureOwnerID, int structureHealthPercentage, String item, String[] resourceImages, String[] resourceData, boolean prospectMode)
	{
		this.visibilityMap.refreshTile(x,y,this.clock.getTick(), structure, structureOwnerID, structureHealthPercentage, item, resourceImages, resourceData, prospectMode);
	}	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Discovers area of tiles with center at [x,y] coordinates and certain radius,
	 * and updates each tiles "last viewed" status and data
	 **************************************************************************************/
	public void refreshArea(int x, int y, int radius, boolean prospectMode)
	{
		LinkedList<int[]> area = getArea(x,y,radius);
		//check area size
		if(area.getSize()>0)
		{
			//reset list to position 0
			area.setCurrent(0);
			//for each coordinates in the list refresh a tile
			for(int i=0; i<area.getSize();i++)
			{
				this.refreshTile(area.peekCurrent()[0], area.peekCurrent()[1], prospectMode);
				area.next();
			}
		}
	}
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns area given x, y and radius
	 **************************************************************************************/
	public LinkedList<int[]> getArea(int x, int y, int radius)
	{
		return this.map.getArea(x,y,radius);
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the array of amounts of owned resources by the player
	 **************************************************************************************/	
	public int[] getPlayerResources()
	{
		return this.player.getResources();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the number of owned workers
	 **************************************************************************************/	
	public int getWorkerCount()
	{
		return this.player.getWorkerCount();
	}	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the clocks tick
	 **************************************************************************************/	
	public int getTick()
	{
		return this.clock.getTick();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the clocks time
	 **************************************************************************************/	
	public String getTime()
	{
		return this.clock.getTime();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the cursor position
	 **************************************************************************************/	
	public int[] getCursorPosition()
	{
		return new int[]{this.cursorX,this.cursorY};
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the cursor position
	 **************************************************************************************/	
	public void setCursorPosition(int x, int y)
	{
		if(this.verifyCursor(x,y))
		{
		this.cursorX=x;
		this.cursorY=y;
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Check if the cursor is within map bounds
	 **************************************************************************************/
	public boolean verifyCursor(int x, int y)
	{
		if((x>=0) && (y>=0) && (x<this.width) && (y<this.height))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Moves the cursor by 1 tile in one of 6 directions
	 **************************************************************************************/	
	public void moveCursorPosition(int direction)
	{
		int newX, newY;
		if(direction==0) //up
		{
			newX=this.cursorX;
			newY=this.cursorY-1;
		}
		else if(direction==1) //up-right
		{
			if((this.cursorX % 2) == 0)
			{
				newX=this.cursorX+1;
				newY=this.cursorY-1;
			}
			else
			{
				newX=this.cursorX+1;
				newY=this.cursorY;
			}
		}
		else if(direction==2) //down-right
		{
			if((this.cursorX % 2) == 0)
			{
				newX=this.cursorX+1;
				newY=this.cursorY;
			}
			else
			{
				newX=this.cursorX+1;
				newY=this.cursorY+1;
			}
		}
		else if(direction==3) //down
		{
				newX=this.cursorX;
				newY=this.cursorY+1;
		}
		else if(direction==4) //down-left
		{
			if((this.cursorX % 2) == 0)
			{
				newX=this.cursorX-1;
				newY=this.cursorY;
			}
			else
			{
				newX=this.cursorX-1;
				newY=this.cursorY+1;
			}
		}
		else if(direction==5) //top-left
		{
			if((this.cursorX % 2) == 0)
			{
				newX=this.cursorX-1;
				newY=this.cursorY-1;
			}
			else
			{
				newX=this.cursorX-1;
				newY=this.cursorY;
			}
		}
		else
		{
			newX=this.cursorX;
			newY=this.cursorY;
		}
		
		if(this.verifyCursor(newX,newY))
		{
		this.cursorX=newX;
		this.cursorY=newY;
		}	
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Moves the cursor to position of currently selected controllable
	 **************************************************************************************/	
	public void moveCursorOnSelected()
	{
		if(this.player.getSelected()!=null)
		{
			int newX=this.player.getSelected().getX();
			int newY=this.player.getSelected().getY();
			if(this.verifyCursor(newX, newY))
			{
				this.setCursorPosition(newX, newY);
			}
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz, Changmin Bae
	 * places the controllable on the map
	 **************************************************************************************/	
	public void putControllableOnMap(int x, int y, Controllable controllable, boolean revealMap)
	{
		this.map.putControllableOnTile(x,y,controllable);
		if(revealMap){this.refreshTile(x, y, false);}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * turn on and off display of resource amounts
	 **************************************************************************************/
	public void toggleDisplayResourceMode()
	{
		this.displayResourceMode++;
		if(this.displayResourceMode>2)
		{
			this.displayResourceMode=0;
		}
	}
	
	public void selectionCycling(int depth, int direction){
		player.selectionCycling(depth, direction);
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae, Leszek Karcz
	 * sends currently selected command to Controllable to add to mission queue
	 **************************************************************************************/
	public void executeSelected(String mode)
	{
		boolean flag=player.getSelected().enqueueSelectedCommand(mode,map,this.cursorX, this.cursorY);
	}
	
	public void removeControllableFromMap(Controllable c)
	{
		map.removeControllable(c);
	}
}
